import math
from mathutils import Vector


class BBox:
    @classmethod
    def calc_bbox(cls, coords):
        xmin = math.inf
        xmax = -math.inf
        ymin = math.inf
        ymax = -math.inf

        for x, y in coords:
            if x < xmin:
                xmin = x
            if x > xmax:
                xmax = x
            if y < ymin:
                ymin = y
            if y > ymax:
                ymax = y
        return cls(xmin, xmax, ymin, ymax)

    @classmethod
    def calc_bbox_uv(cls, group, uv_layers, are_loops=False):
        xmin = math.inf
        xmax = -math.inf
        ymin = math.inf
        ymax = -math.inf

        if not are_loops:
            for face in group:
                for loop in face.loops:
                    x, y = loop[uv_layers].uv
                    if xmin > x:
                        xmin = x
                    if xmax < x:
                        xmax = x
                    if ymin > y:
                        ymin = y
                    if ymax < y:
                        ymax = y
        else:
            for loop in group:
                x, y = loop[uv_layers].uv
                if xmin > x:
                    xmin = x
                if xmax < x:
                    xmax = x
                if ymin > y:
                    ymin = y
                if ymax < y:
                    ymax = y
        return cls(xmin, xmax, ymin, ymax)

    @classmethod
    def init_from_minmax(cls, min, max):
        bbox = cls(min[0], max[0], min[1], max[1])
        bbox.sanitize()
        return bbox

    def __init__(self, xmin=math.inf, xmax=-math.inf, ymin=math.inf, ymax=-math.inf):
        self.xmin = xmin
        self.xmax = xmax
        self.ymin = ymin
        self.ymax = ymax

    def __str__(self):
        return f"xmin={self.xmin:.6}, xmax={self.xmax:.6}, ymin={self.ymin:.6}, ymax={self.ymax:.6}, width={self.width:.6}, height={self.height:.6}"

    @property
    def is_valid(self) -> bool:
        return (self.xmin <= self.xmax) and (self.ymin <= self.ymax)

    @property
    def max(self):
        return Vector((self.xmax, self.ymax))

    @property
    def min(self):
        return Vector((self.xmin, self.ymin))

    @property
    def left_upper(self):
        return Vector((self.xmin, self.ymax))

    @property
    def left_bottom(self):
        return Vector((self.xmin, self.ymin))

    @property
    def right_bottom(self):
        return Vector((self.xmax, self.ymin))

    @property
    def right_upper(self):
        return Vector((self.xmax, self.ymax))

    @property
    def upper(self):
        return Vector(((self.xmin + self.xmax) * 0.5, self.ymax))

    @property
    def bottom(self):
        return Vector(((self.xmin + self.xmax) * 0.5, self.ymin))

    @property
    def left(self):
        return Vector((self.xmin, (self.ymin + self.ymax) * 0.5))

    @property
    def right(self):
        return Vector((self.xmax, (self.ymin + self.ymax) * 0.5))

    @property
    def center(self):
        return Vector(((self.xmin + self.xmax) * 0.5, (self.ymin + self.ymax) * 0.5))

    @property
    def width(self) -> float:
        return self.xmax - self.xmin

    @property
    def height(self) -> float:
        return self.ymax - self.ymin

    @property
    def max_lenght(self):
        return max(self.width, self.height)

    @property
    def min_lenght(self):
        return min(self.width, self.height)

    @property
    def half_width(self) -> float:
        return (self.xmax - self.xmin) * 0.5

    @property
    def half_height(self) -> float:
        return (self.ymax - self.ymin) * 0.5

    @property
    def area(self):
        return self.width * self.height

    @property
    def is_empty(self) -> bool:
        return (self.xmax <= self.xmin) or (self.ymax <= self.ymin)

    def union(self, other):
        if self.xmin > other.xmin:
            self.xmin = other.xmin
        if self.xmax < other.xmax:
            self.xmax = other.xmax
        if self.ymin > other.ymin:
            self.ymin = other.ymin
        if self.ymax < other.ymax:
            self.ymax = other.ymax
        return self

    def sanitize(self):
        if self.xmin > self.xmax:
            self.xmin, self.xmax = self.xmax, self.xmin
        if self.ymin > self.ymax:
            self.ymin, self.ymax = self.ymax, self.ymin
        # assert self.is_valid
        return self

    def do_minmax_v(self, xy):
        if xy[0] < self.xmin:
            self.xmin = xy[0]
        if xy[0] > self.xmax:
            self.xmax = xy[0]
        if xy[1] < self.ymin:
            self.ymin = xy[1]
        if xy[1] > self.ymax:
            self.ymax = xy[1]

    def update(self, coords):
        for x, y in coords:
            if x < self.xmin:
                self.xmin = x
            if x > self.xmax:
                self.xmax = x
            if y < self.ymin:
                self.ymin = y
            if y > self.ymax:
                self.ymax = y
